#include "xmppBot.h"
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#define send_xmpp_hb(_parser,_from,to,sub,_msg) \
do {\
  iks *_reply = iks_new("message");\
  if( !_reply ) {\
    break;\
  }\
  iks_insert_attrib(_reply, "from", _from);\
  iks_insert_attrib(_reply, "id", "xrp-muc-hb-1id");\
  iks_insert_attrib(_reply, "to", to );\
  iks_insert_attrib(_reply, "type", "groupchat");\
  iks_insert_cdata(iks_insert(_reply, "subject"), sub, 0);\
  iks_insert_cdata(iks_insert(_reply, "body"), _msg, 0);\
  iks_send(_parser, _reply);\
  iks_delete(_reply);\
} while(0)

#define send_xmpp_alert(_parser,_from,to,_msg) \
do {\
  iks *_reply = iks_new("message");\
  if( !_reply ) {\
    break;\
  }\
  iks_insert_attrib(_reply, "from", _from);\
  iks_insert_attrib(_reply, "id", "xrp-muc-alt-1id");\
  iks_insert_attrib(_reply, "to", to );\
  iks_insert_attrib(_reply, "type", "groupchat");\
  iks_insert_cdata(iks_insert(_reply, "subject"), "Alert", 0);\
  iks_insert_cdata(iks_insert(_reply, "body"), _msg, 0);\
  iks_send(_parser, _reply);\
  iks_delete(_reply);\
} while(0)

xmppBot::xmppBot(IoOps *iops, xmppFilter *filter,
                bool t,xmppLogger *logger):useTls(t),
                _authorized(false),_error(false), _features(0),
                log(logger),_interval(-1), publishHeartbeat(true)
{
  this->absFilter = filter;
  this->io = iops;
}

xret
xmppBot::setJID(std::string &j, std::string &passwd)
{
  this->jid = j;
  this->j_password = passwd;
  return xp_OK;
}

xret
xmppBot::processXMPP(int timeout)
{
  int expire = timeout;
  time_t begin_wclock = 0;
  time_t end_wclock = 0;

  if( xp_OK != xmpp_init()) return xp_FAIL;

  do {
    char hbMsg[XP_XMPP_HB_BUF_MAX] = {0};
    size_t len = XP_XMPP_HB_BUF_MAX;
    char hbTo[XP_XMPP_TO_JID_MAX] = {0};
    size_t to_len = XP_XMPP_TO_JID_MAX;
    ioevent ev = 0;
    ioevent wret = 0;
    begin_wclock = time(NULL);

    wret = this->io->watch((ioevent)XP_IOPS_READ, ev, expire);

    if( xp_OK != wret && xp_TIMEOUT != wret )
      return xp_FAIL;

    if( (ev & XP_IOPS_ERROR) || (ev & XP_IOPS_EOF) )
      return xp_FAIL;

    if((ev & XP_IOPS_READ ) ) {
      /*OOPs stream error or reset from server*/
      if( IKS_NET_RWERR == iks_recv(this->_parser,0) ) {
        this->io->close();
        continue;
      }
    }
#if 0
    if( (ev & XP_IOPS_ALERT) ) {
      int rfd = -1;
    }
#endif

    end_wclock = time(NULL);
    

    if( (end_wclock - begin_wclock) >= expire ) {
      /*Heartbeat expires*/
      app_debug("%s","Exec HeartBeat");
      expire = timeout;
    } else {
      expire = (expire - (end_wclock - begin_wclock));
    }
    app_debug("Sec to heartbeat [%d]", expire );

  } while( !this->_error );

  return xp_OK;
}

/*protected area*/
#if defined(__NEED_DEBUG__)
void
xmppBot::xmpp_hook_log(xmppBot *xb, const char *data, size_t size,
                int is_incoming)
{
  char *dir = "RECV";

  if(iks_is_secure(xb->_parser)) {
    fprintf (stderr, "Sec");
  }
  if(is_incoming) {
    fprintf(stderr, "RECV");
  } else {
    fprintf (stderr, "SEND");
    dir = "SEND";
  }
  if( xb->log )
    xb->log->debug("%s : %s", dir, data);
  fprintf(stderr, "[%s]\n", data);
}
#endif

int
xmppBot::xmpp_hook_stream(xmppBot *xb, int type, iks *node)
{
  switch (type) {
    case IKS_NODE_START: {
      if(xb->useTls && !iks_is_secure(xb->_parser)) {
        if(IKS_OK != iks_start_tls(xb->_parser) ) {
          if(xb->log)
            xb->log->fatal("%s","TLS Required but no support found");
        }
        break;
      }
    }
    break;
    case IKS_NODE_NORMAL: {
      if(strcmp ("stream:features", iks_name(node)) == 0) {
        xb->_features = iks_stream_features(node);
        if(xb->useTls && !iks_is_secure(xb->_parser)) break;
        if(xb->_authorized) {
          iks *t;
          if(xb->_features & IKS_STREAM_BIND) {
            t = iks_make_resource_bind(xb->_account);
            iks_send(xb->_parser, t);
            iks_delete (t);
          }
          if(xb->_features & IKS_STREAM_SESSION) {
            t = iks_make_session();
            iks_insert_attrib(t, "id", XP_IQ_SESSION_ID);
            iks_send(xb->_parser, t);
            iks_delete (t);
          }
        } else {
          if(xb->_features & IKS_STREAM_SASL_MD5)
            iks_start_sasl(xb->_parser, IKS_SASL_DIGEST_MD5, xb->_account->user,
                            (char*)xb->j_password.c_str());
          else if(xb->_features & IKS_STREAM_SASL_PLAIN)
            iks_start_sasl(xb->_parser, IKS_SASL_PLAIN,
                            xb->_account->user, (char*)xb->j_password.c_str());
        }
      } else if (strcmp ("failure", iks_name (node)) == 0) {
        /*in-bound auto registration*/
          if( xb->log )
            xb->log->warn("%s","sasl authentication FAILED;"
                          " proceeding with InBound Registration");
      } else if (strcmp ("success", iks_name (node)) == 0) {
        xb->_authorized = true;
        iks_send_header(xb->_parser, xb->_account->server);
        if( xb->log )
          xb->log->debug("%s","Authentication PASSED!");
      } else {
        ikspak *pak = NULL;
        pak = iks_packet(node);
        iks_filter_packet(xb->_filters, pak);

#if 0
        /* if inbound registration went well then resend the login info*/
        if( xp_OK == xb->inbound_register_response(node) ) {
          if(xb->_features & IKS_STREAM_SASL_MD5)
            iks_start_sasl(xb->_parser, IKS_SASL_DIGEST_MD5, xb->_account->user,
                            (char*)xb->j_password.c_str());
          else if(xb->_features & IKS_STREAM_SASL_PLAIN)
            iks_start_sasl(xb->_parser, IKS_SASL_PLAIN,
                            xb->_account->user, (char*)xb->j_password.c_str());

        } else {
        }
#endif
      }
    }
    break;
    case IKS_NODE_STOP:
      app_debug("%s","Server Disconnected");
    break;
    case IKS_NODE_ERROR:
      app_debug("%s","Stream Error");
    break;
  }
  if( node ) iks_delete( node );
  return xp_OK;
}

xret
xmppBot::xmpp_init(void)
{
  int fd = -1;

  if( xp_OK != io->getFD( &fd ) )
    return xp_FAIL;

  this->_parser  = iks_stream_new(IKS_NS_CLIENT, this, (iksStreamHook*)xmpp_hook_stream);
  this->absFilter->setupFilter( this->_parser );
  this->absFilter->getFilter(&(this->_filters) );

  if( NULL == this->_filters ) {
    iks_parser_delete(this->_parser);
    return xp_FAIL;
  }
#if defined(__NEED_DEBUG__)
  iks_set_log_hook(this->_parser,(iksLogHook *)xmpp_hook_log);
#endif 
  this->_account = iks_id_new(iks_parser_stack(this->_parser),this->jid.c_str());
  if(NULL == this->_account->resource) {
    char *tmp = NULL;
    iks_free( this->_account );
    size_t _len = strlen(this->_account->user) + strlen(this->_account->server) + 10 + 4;
    tmp = (char*)iks_malloc(sizeof(char) * _len);
    sprintf(tmp, "%s@%s/%d", this->_account->user, this->_account->server, (int)getpid());
    this->_account = iks_id_new(iks_parser_stack(this->_parser), tmp);
    iks_free(tmp);
  }
  switch(iks_connect_fd(this->_parser,fd)) {
    case IKS_OK: {
        iks_send_header(this->_parser,this->_account->server);
        return xp_OK;
      }
      break;
    default:
        return xp_FAIL;
  }
  return xp_FAIL;
}

/*
vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=4:softtabstop=2:encoding=utf-8:textwidth=80
*/
